package com.meta.nd.dao.impl;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;
import org.hibernate.Query;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.transform.Transformers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import com.meta.cache.NdCacheService;
import com.meta.common.ConstDef;
import com.meta.counter.service.CounterService;
import com.meta.nd.dao.NvDAO;
import com.meta.nd.util.NdDbMapping;
import com.meta.nd.util.NodeUtil;
import com.meta.nd.vo.Fd;
import com.meta.nd.vo.FieldCondition;
import com.meta.nd.vo.Fv;
import com.meta.nd.vo.ListQueryCondition;
import com.meta.nd.vo.ListQueryResult;
import com.meta.nd.vo.Nd;
import com.meta.nd.vo.Nv;
import org.springframework.util.StringUtils;

@Slf4j
@Transactional
@Component
public class NvDAOImpl implements NvDAO {
    @Autowired
    private SessionFactory sessionFactory;

    @Autowired
    CounterService counterService;

    @Autowired
    NdCacheService ndCacheService;

    @Override
    public long saveNv(Nv nv) {
        String dbMapCode = nv.getNd().getDbMapCode();
        NdDbMapping ndDbMapping = this.ndCacheService.getNdDbMapping(dbMapCode);
        try {
            if (nv.getOperationFlag().equals(ConstDef.OPERATION_FLAG_UPDATE)) {
                this.updateNv(nv, ndDbMapping);
                return nv.getId();
            } else
                return this.insertNv(nv, ndDbMapping);
        } catch (Exception e) {
            String log4jPath = NvDAOImpl.class.getClassLoader().getResource("").getPath() + "/log4j.properties";
            System.out.println("初始化Log4j。。。。");
            System.out.println("path is " + log4jPath);
            PropertyConfigurator.configure(log4jPath);
            Logger logger = Logger.getLogger(NvDAOImpl.class);
            logger.error("可能配置错误\n" + ndDbMapping.getUpdateSql() + "\n" + ndDbMapping.getInsertSql());
            e.printStackTrace();
            throw e;
        }
    }


    @Override
    public int deleteNv(String ndCode, long id) {
        Nd nd = this.ndCacheService.getNd(ndCode);
        String dbMapCode = nd.getDbMapCode();
        Session session = this.sessionFactory.getCurrentSession();
        List<Nd> childNds = nd.getChildren();
        if (nd.isCursive()) {
            childNds = new ArrayList<>();
            childNds.addAll(nd.getChildren());
            childNds.add(nd);
        }
        for (Nd childNd : childNds) {
            if (StringUtils.isEmpty(childNd.getDbMapCode()))
                continue;
            NdDbMapping childMapping = ndCacheService.getNdDbMapping(childNd.getDbMapCode());
            String selectSql = " select %s from %s where %s = :%s and %s = :%s";
            selectSql = String.format(selectSql, ConstDef.FD_NAME_ID, childMapping.getNvTableName(), ConstDef.FD_NAME_PARENT_ID, ConstDef.FD_NAME_PARENT_ID, ConstDef.FD_NAME_META_ND_CODE, ConstDef.FD_NAME_META_ND_CODE);
            Query selectQ = session.createSQLQuery(selectSql);
            selectQ.setLong(ConstDef.FD_NAME_PARENT_ID, id);
            selectQ.setString(ConstDef.FD_NAME_META_ND_CODE, childNd.getNdCode());
            List<Map<String, Object>> resultList = (List<Map<String, Object>>) selectQ.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
            for (Map<String, Object> record : resultList) {
                deleteNv(childNd.getNdCode(), ((BigInteger) (record.get(ConstDef.FD_NAME_ID))).longValue());
            }
        }
        NdDbMapping ndDbMapping = ndCacheService.getNdDbMapping(dbMapCode);
        String sql = this.createNvDeleteSql(ndDbMapping.getNvTableName());
        Query q = session.createSQLQuery(sql);
        q.setLong(ConstDef.FD_NAME_ID, id);
        int count = q.executeUpdate();
        if (ndDbMapping.getFvTableName() != null && !ndDbMapping.getFvTableName().equals("")) {
            String deleteFvSql = "delete from " + ndDbMapping.getFvTableName() + " where %s = :%s";
            deleteFvSql = String.format(deleteFvSql, ConstDef.FD_NAME_NV_ID, ConstDef.FD_NAME_NV_ID);
            Query deleteFvQuery = session.createSQLQuery(deleteFvSql);
            deleteFvQuery.setLong(ConstDef.FD_NAME_NV_ID, id);
            deleteFvQuery.executeUpdate();
        }
        return count;
    }

    public void deleteNvByParentId(Nd rootNd, String code, String type, long parentId) {
        NdDbMapping ndDbMapping = ndCacheService.getNdDbMapping(type);
        String sql = this.createNvByParentDeleteSql(ndDbMapping.getNvTableName());
        Session session = this.sessionFactory.getCurrentSession();
        Query q = session.createSQLQuery(sql);
        q.setLong(ConstDef.FD_NAME_PARENT_ID, parentId);
        int count = q.executeUpdate();
    }


    private long insertFv(long nvId, Fv fv, String tableName) {
        long id = counterService.nextTableId();
        fv.setId(id);
        Session session = this.sessionFactory.getCurrentSession();
        Query q = null;
        String sql = null;
        String dataType = fv.getDataType();
        if (dataType.equals(ConstDef.DATA_TYPE_INT)) {
            sql = createIntFvInsertSql(tableName);
            q = session.createSQLQuery(sql);
            q.setInteger("intValue", fv.getRawInt());

        } else if (dataType.equals(ConstDef.DATA_TYPE_LONG)) {
            sql = createIntFvInsertSql(tableName);
            q = session.createSQLQuery(sql);
            q.setLong("intValue", fv.getRawLong());
        } else if (dataType.equals(ConstDef.DATA_TYPE_FLOAT)) {
            sql = createDoubleFvInsertSql(tableName);
            q = session.createSQLQuery(sql);
            q.setDouble("doubleValue", fv.getRawDouble());
        } else if (dataType.equals(ConstDef.DATA_TYPE_STRING)) {
            sql = createStringFvInsertSql(tableName);
            q = session.createSQLQuery(sql);
            q.setString("stringValue", fv.getRawString());
        } else if (dataType.equals(ConstDef.DATA_TYPE_BOOLEAN)) {
            sql = createBoolFvInsertSql(tableName);
            q = session.createSQLQuery(sql);
            q.setBoolean("boolValue", fv.getRawBool());
        } else if (dataType.equals(ConstDef.DATA_TYPE_DATE)) {
            sql = createDateFvInsertSql(tableName);
            q = session.createSQLQuery(sql);
            q.setDate("dateValue", fv.getRawDate());
        } else if (dataType.equals(ConstDef.DATA_TYPE_DATETIME)) {
            sql = createDateFvInsertSql(tableName);
            q = session.createSQLQuery(sql);
            q.setTimestamp("dateValue", fv.getRawDate());
        }

        q.setLong(ConstDef.FD_NAME_ID, id);
        q.setString(ConstDef.FD_NAME_CODE, fv.getCode());
        q.setString(ConstDef.FD_NAME_NAME, fv.getName());
        q.setString(ConstDef.FD_NAME_DATA_TYPE, fv.getDataType());
        q.setLong(ConstDef.FD_NAME_NV_ID, nvId);
        int count = q.executeUpdate();
        if (count < 1)
            throw new RuntimeException("执行插入错误：" + sql);
        return id;
    }


    private void updateFv(long nvId, Fv fv, String tableName) {
        Session session = this.sessionFactory.getCurrentSession();
        Query q = null;
        String sql;
        String dataType = fv.getDataType();
        if (dataType.equals(ConstDef.DATA_TYPE_INT)) {
            sql = createIntFvUpdateSql(tableName);
            q = session.createSQLQuery(sql);
            q.setInteger("intValue", fv.getRawInt());
        } else if (dataType.equals(ConstDef.DATA_TYPE_LONG)) {
            sql = createIntFvUpdateSql(tableName);
            q = session.createSQLQuery(sql);
            q.setLong("intValue", fv.getRawLong());
        } else if (dataType.equals(ConstDef.DATA_TYPE_FLOAT)) {
            sql = createDoubleFvUpdateSql(tableName);
            q = session.createSQLQuery(sql);
            q.setDouble("doubleValue", fv.getRawDouble());
        } else if (dataType.equals(ConstDef.DATA_TYPE_STRING)) {
            sql = createStringFvUpdateSql(tableName);
            q = session.createSQLQuery(sql);
            q.setString("stringValue", fv.getRawString());
        } else if (dataType.equals(ConstDef.DATA_TYPE_BOOLEAN)) {
            sql = createBoolFvUpdateSql(tableName);
            q = session.createSQLQuery(sql);
            q.setBoolean("boolValue", fv.getRawBool());
        } else if (dataType.equals(ConstDef.DATA_TYPE_DATE)) {
            sql = createDateFvUpdateSql(tableName);
            q = session.createSQLQuery(sql);
            q.setDate("dateValue", fv.getRawDate());
        } else if (dataType.equals(ConstDef.DATA_TYPE_DATETIME)) {
            sql = createDateFvUpdateSql(tableName);
            q = session.createSQLQuery(sql);
            q.setTimestamp("dateValue", fv.getRawDate());
        }
        q.setString(ConstDef.FD_NAME_CODE, fv.getCode());
        q.setString(ConstDef.FD_NAME_NAME, fv.getName());
        q.setString(ConstDef.FD_NAME_DATA_TYPE, fv.getDataType());
        q.setLong(ConstDef.FD_NAME_NV_ID, nvId);
        int count = q.executeUpdate();
        if (count < 1) {
            this.insertFv(nvId, fv, tableName);
        }
    }


    private String createIntFvUpdateSql(String tableName) {
        return "update " + tableName + " set code=:code,dataType=:dataType,intValue=:intValue where nvId = :nvId and name = :name";
    }

    private String createDoubleFvUpdateSql(String tableName) {

        return "update " + tableName + " set code=:code, dataType=:dataType,doubleValue=:doubleValue where nvId = :nvId and name = :name";
    }


    private String createStringFvUpdateSql(String tableName) {
        return "update " + tableName + " set code=:code, dataType=:dataType,stringValue=:stringValue where nvId = :nvId and name = :name";
    }

    private String createDateFvUpdateSql(String tableName) {
        return "update " + tableName + " set code=:code, dataType=:dataType,dateValue=:dateValue where nvId = :nvId and name = :name";
    }

    private String createBoolFvUpdateSql(String tableName) {
        return "update " + tableName + " set code=:code, dataType=:dataType,boolValue=:boolValue where nvId = :nvId and name = :name";
    }

    private String createIntFvInsertSql(String tableName) {
        return "insert into " + tableName + " ( id, nvId, code,name,dataType,intValue) values( :id,:nvId, :code,:name,:dataType,:intValue)";
    }


    private String createDoubleFvInsertSql(String tableName) {

        return "insert into " + tableName + " ( id, nvId, code,name,dataType,doubleValue) values( :id,:nvId, :code,:name,:dataType,:doubleValue)";
    }

    private String createStringFvInsertSql(String tableName) {
        return "insert into " + tableName + " ( id, nvId, code,name,dataType,stringValue) values( :id,:nvId, :code,:name,:dataType,:stringValue)";
    }

    private String createDateFvInsertSql(String tableName) {

        return "insert into " + tableName + " ( id, nvId, code,name,dataType,dateValue) values( :id,:nvId, :code,:name,:dataType,:dateValue)";
    }

    private String createBoolFvInsertSql(String tableName) {
        return "insert into " + tableName + " ( id, nvId, code,name,dataType,boolValue) values( :id,:nvId, :code,:name,:dataType,:boolValue)";
    }

    private String createFvDeleteSql(String tableName) {
        return " delete from " + tableName + " where id = :id";
    }

    private String createFvDeleteByNvSql(String tableName) {
        return " delete from " + tableName + " where nvId = :nvId";
    }


    private String createNvDeleteSql(String tableName) {
        return " delete from " + tableName + " where id = :id";
    }

    private String createNvByParentDeleteSql(String tableName) {

        return " delete from " + tableName + " where parentId = :parentId";

    }


    private void setQueryData(String name, String dataType, Nv nv, Query q) {

        if (dataType.equals(ConstDef.DATA_TYPE_INT))
            q.setInteger(name, nv.getRawIntByName(name));
        else if (dataType.equals(ConstDef.DATA_TYPE_LONG))
            q.setLong(name, nv.getRawLongByName(name));
        else if (dataType.equals(ConstDef.DATA_TYPE_FLOAT))
            q.setDouble(name, nv.getRawDoubleByName(name));
        else if (dataType.equals(ConstDef.DATA_TYPE_STRING))
            q.setString(name, nv.getRawStringByName(name));
        else if (dataType.equals(ConstDef.DATA_TYPE_BOOLEAN))
            q.setBoolean(name, nv.getRawBoolByName(name));
        else if (dataType.equals(ConstDef.DATA_TYPE_DATE))
            q.setDate(name, nv.getRawDateByName(name));
        else if (dataType.equals(ConstDef.DATA_TYPE_DATETIME))
            q.setTimestamp(name, nv.getRawDateByName(name));
        else if (dataType.equals(ConstDef.DATA_TYPE_STRING_LIST)) {
            q.setString(name, nv.getFvByName(name).stringList2String());
        } else
            throw new RuntimeException("不存在的数据类型！");
    }


    private long insertNv(Nv nv, NdDbMapping ndDbMapping) {
        long id = nv.getId();
        if (id <= 0) {
            id = this.counterService.nextTableId();
            nv.setId(id);
        }

        String sql = ndDbMapping.getInsertSql();
        Nd nd = nv.getNd();
        Session session = sessionFactory.getCurrentSession();
        Query q = session.createSQLQuery(sql);
        q.setLong(ConstDef.FD_NAME_ID, id);
        q.setString(ConstDef.FD_NAME_META_ND_CODE, nd.getNdCode());
        if (nv.getParentId() > 0) {
            q.setLong(ConstDef.FD_NAME_PARENT_ID, nv.getParentId());
        } else if (nv.getParent() != null)
            q.setLong(ConstDef.FD_NAME_PARENT_ID, nv.getParentId());
        else
            q.setLong(ConstDef.FD_NAME_PARENT_ID, 0);

        for (String name : ndDbMapping.getNvFieldNames()) {
            String dataType = nd.getFdByName(name).getDataType();
            setQueryData(name, dataType, nv, q);
        }
        int count = q.executeUpdate();
        if (count < 1) {
            throw new RuntimeException("插入记录失败：" + sql);
        }
        if (!ndDbMapping.isPersistRemainFields() && ndDbMapping.getFvFieldNames() != null && ndDbMapping.getFvFieldNames().size() > 0) {
            for (String name : ndDbMapping.getFvFieldNames()) {
                Fv fv = nv.getFvByName(name);
                insertFv(id, fv, ndDbMapping.getFvTableName());
            }
        } else if (ndDbMapping.isPersistRemainFields()) {
            List<String> fieldNames = nd.getFieldNames();
            fieldNames.removeAll(ndDbMapping.getNvFieldNames());
            for (String name : fieldNames) {
                Fv fv = nv.getFvByName(name);
                insertFv(id, fv, ndDbMapping.getFvTableName());
            }
        }
        nv.setOperationFlag(ConstDef.OPERATION_FLAG_UNCHANGED);
        return id;
    }


    private void updateNv(Nv nv, NdDbMapping ndDbMapping) {
        String sql = ndDbMapping.getUpdateSql();
        Nd nd = nv.getNd();
        Session session = sessionFactory.getCurrentSession();
        Query q = session.createSQLQuery(sql);
        q.setLong(ConstDef.FD_NAME_ID, nv.getId());
        q.setString(ConstDef.FD_NAME_META_ND_CODE, nd.getNdCode());
        if (nv.getParentId() > 0) {
            q.setLong(ConstDef.FD_NAME_PARENT_ID, nv.getParentId());
        } else if (nv.getParent() == null)
            q.setLong(ConstDef.FD_NAME_PARENT_ID, 0);
        else
            q.setLong(ConstDef.FD_NAME_PARENT_ID, nv.getParent().getId());
        for (String name : ndDbMapping.getNvFieldNames()) {
            String dataType = nd.getFdByName(name).getDataType();
            setQueryData(name, dataType, nv, q);
        }
        int count = q.executeUpdate();
        if (count < 1) {
            throw new RuntimeException("更新记录失败：" + sql);
        }
        if (!ndDbMapping.isPersistRemainFields() && ndDbMapping.getFvFieldNames() != null && ndDbMapping.getFvFieldNames().size() > 0) {
            for (String name : ndDbMapping.getFvFieldNames()) {
                Fv fv = nv.getFvByName(name);
                updateFv(nv.getId(), fv, ndDbMapping.getFvTableName());
            }
        } else if (ndDbMapping.isPersistRemainFields()) {
            List<String> fieldNames = nd.getFieldNames();
            fieldNames.removeAll(ndDbMapping.getNvFieldNames());
            for (String name : fieldNames) {
                Fv fv = nv.getFvByName(name);
                updateFv(nv.getId(), fv, ndDbMapping.getFvTableName());
            }
        }
        nv.setOperationFlag(ConstDef.OPERATION_FLAG_UNCHANGED);
    }


    @Override
    public List<Long> saveNvs(List<Nv> nvs) {

        List<Long> ids = new ArrayList<Long>();

        for (Nv nv : nvs) {

            if (!ConstDef.OPERATION_FLAG_UNCHANGED.equals(nv.getOperationFlag())) {
                this.saveNv(nv);
            }

            ids.add(nv.getId());
        }
        return ids;
    }

    private Nv record2Nv(Nd rootNd, Map<String, Object> record, NdDbMapping ndDbMapping) {
        String code = (String) record.get(ConstDef.FD_NAME_META_ND_CODE);
        Nd nd = rootNd.getDescendant(code);
        Nv nv = NodeUtil.nd2Nv(nd);
        long id = ((BigInteger) record.get(ConstDef.FD_NAME_ID)).longValue();
        long parentId = 0L;
        if (record.get(ConstDef.FD_NAME_PARENT_ID) != null)
            parentId = ((BigInteger) record.get(ConstDef.FD_NAME_PARENT_ID)).longValue();
        nv.setId(id);
        nv.setParentId(parentId);
        nv.setMetaNdCode(code);
        nv.setActualNdCode(code);
        for (String name : ndDbMapping.getNvFieldNames()) {
            Object value = record.get(name);
            Fd fd = nd.getFdByName(name);
            if (fd.getDataType().equals(ConstDef.DATA_TYPE_LONG)) {
                long longValue = 0;
                if (value != null)
                    longValue = ((BigInteger) value).longValue();
                nv.setFvByName(name, longValue);
            } else if (fd.getDataType().equals(ConstDef.DATA_TYPE_STRING_LIST)) {
                String strValue = (String) value;
                List<String> strList = new ArrayList<>();
                if (StringUtils.isEmpty(strValue))
                    nv.setFvByName(name, strList);
                else {
                    String[] arr = strValue.split(",");
                    for (String str : arr)
                        strList.add(str);

                    nv.setFvByName(name, strList);
                }
            } else
                nv.setFvByName(name, value);

        }
        nv.setOperationFlag(ConstDef.OPERATION_FLAG_UNCHANGED);
        return nv;
    }


    private Fv record2Fv(Nd nd, Map<String, Object> record, NdDbMapping ndDbMapping) {
        String name = (String) record.get(ConstDef.FD_NAME_NAME);
        Fd fd = nd.getFdByName(name);
        if (fd == null) return null;
        Fv fv = NodeUtil.fd2Fv(fd);
        long id = ((BigInteger) record.get(ConstDef.FD_NAME_ID)).longValue();
        fv.setId(id);
        fv.setCode((String) record.get(ConstDef.FD_NAME_CODE));
        fv.setDataType((String) record.get(ConstDef.FD_NAME_DATA_TYPE));
        fv.setName((String) record.get(ConstDef.FD_NAME_NAME));
        Object value = null;
        String dataType = fd.getDataType();
        if (dataType.equals(ConstDef.DATA_TYPE_LONG)) {
            value = record.get("intValue");
            long longValue = 0;
            if (value != null)
                longValue = ((BigInteger) value).longValue();
            fv.setRawLong(longValue);
        } else if (dataType.equals(ConstDef.DATA_TYPE_INT)) {
            value = record.get("intValue");
            int intValue = 0;
            if (value != null)
                intValue = ((BigInteger) value).intValue();
            fv.setRawInt(intValue);
        } else if (dataType.equals(ConstDef.DATA_TYPE_FLOAT)) {
            value = record.get("floatValue");
            double doubleValue = 0.0;
            if (value != null)
                doubleValue = ((Double) value).doubleValue();
            fv.setRawDouble(doubleValue);
        } else if (dataType.equals(ConstDef.DATA_TYPE_STRING)) {
            value = record.get("stringValue");
            fv.setValue(value);
        } else if (dataType.equals(ConstDef.DATA_TYPE_BOOLEAN)) {
            value = record.get("boolValue");
            fv.setValue(value);
        } else if (dataType.equals(ConstDef.DATA_TYPE_DATE)) {
            value = record.get("dateValue");
            fv.setValue(value);
        } else if (dataType.equals(ConstDef.DATA_TYPE_DATETIME)) {
            value = record.get("dateValue");
            fv.setValue(value);
        } else {
            throw new RuntimeException("不存在的数据类型：" + dataType);
        }
        return fv;
    }

    private List<Nv> getChildNvs(String rootNdCode, String dbMapCode, String nodeCode, long parentId) {
        NdDbMapping ndDbMapping = ndCacheService.getNdDbMapping(dbMapCode);
        String sql = ndDbMapping.getSelectSql() + " where %s = :%s and %s = :%s";
        sql = String.format(sql, ConstDef.FD_NAME_PARENT_ID, ConstDef.FD_NAME_PARENT_ID, ConstDef.FD_NAME_META_ND_CODE, ConstDef.FD_NAME_META_ND_CODE);
        Session session = this.sessionFactory.getCurrentSession();
        Query q = session.createSQLQuery(sql);
        q.setLong(ConstDef.FD_NAME_PARENT_ID, parentId);
        q.setString(ConstDef.FD_NAME_META_ND_CODE, nodeCode);
        List<Map<String, Object>> resultList = (List<Map<String, Object>>) q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
        List<Nv> nvs = new ArrayList<Nv>();
        Nd nd = this.ndCacheService.getRootNd(rootNdCode);
        Nd currentNd = nd.getDescendant(nodeCode);
        for (Map<String, Object> record : resultList) {
            Nv nv = this.record2Nv(nd, record, ndDbMapping);
            if (ndDbMapping.getFvTableName() != null && !ndDbMapping.getFvTableName().equals("")) {
                Map<String, Fv> fvs = this.getFvs(currentNd, nv.getId());
                for (Fv fv : fvs.values())
                    nv.setFv(fv);
            }
            nvs.add(nv);
        }
        for (Nv childNv : nvs) {
            List<Nd> childNdList = currentNd.getChildren();
            for (Nd childNd : childNdList) {
                if (StringUtils.isEmpty(childNd.getDbMapCode()))
                    continue;
                List<Nv> childChildren = getChildNvs(rootNdCode, childNd.getDbMapCode(), childNd.getNdCode(), childNv.getId());
                childNv.setChildNv(childNd.getNdName(), childChildren);
            }
            if (currentNd.isCursive()) {
                List<Nv> childChildren = getChildNvs(rootNdCode, currentNd.getDbMapCode(), currentNd.getNdCode(), childNv.getId());
                childNv.setChildNv(currentNd.getNdName(), childChildren);
            }
        }
        return nvs;
    }

    @Override
    public Nv getRootNvByCode(String code, String name, String ndCode) {
        Nd nd = ndCacheService.getNd(ndCode);
        String dbMapCode = nd.getDbMapCode();
        NdDbMapping ndDbMapping = ndCacheService.getNdDbMapping(dbMapCode);

        String sql = "select id, metaNdCode from " + ndDbMapping.getNvTableName() + " where " + name + " = :code";

        Session session = this.sessionFactory.getCurrentSession();

        Query q = session.createSQLQuery(sql);

        q.setString("code", code);

        List<Map<String, Object>> resultList = q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();

        if (resultList.size() == 0)
            return null;

        Map<String, Object> record = (Map<String, Object>) resultList.get(0);

        BigInteger id = (BigInteger) record.get(ConstDef.FD_NAME_ID);
        String metaNdCode = (String) record.get(ConstDef.FD_NAME_META_ND_CODE);
        return this.getNv(metaNdCode, id.longValue());

    }

    @Override
    public Nv getSingleNvByCode(String code, String name, String ndCode) {
        Nd nd = ndCacheService.getNd(ndCode);
        NdDbMapping ndDbMapping = ndCacheService.getNdDbMapping(nd.getDbMapCode());
        String sql = "select id,metaNdCode from " + ndDbMapping.getNvTableName() + " where " + name + " = :code";
        Session session = this.sessionFactory.getCurrentSession();
        Query q = session.createSQLQuery(sql);
        q.setString("code", code);
        List<Map<String, Object>> resultList = q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
        if (resultList.size() == 0)
            return null;
        Map<String, Object> record = (Map<String, Object>) resultList.get(0);
        BigInteger id = (BigInteger) record.get(ConstDef.FD_NAME_ID);
        String localNdCode = (String) record.get(ConstDef.FD_NAME_META_ND_CODE);
        Nd localNd = ndCacheService.getNd(localNdCode);
        Nv nv = this.getSingleNvWithoutFv(localNd, id.longValue());
        if (ndDbMapping.getFvTableName() != null && !ndDbMapping.getFvTableName().equals("")) {
            Map<String, Fv> fvs = this.getFvs(nd, nv.getId());
            for (Fv fv : fvs.values()) {
                nv.setFv(fv);
            }
        }
        return nv;
    }

    @Override
    public Nv getSingleNvWithoutFv(String ndCode, long id) {
        Nd nd = this.ndCacheService.getNd(ndCode);
        return this.getSingleNvWithoutFv(nd, id);
    }

    @Override
    public Nv getSingleNvWithoutFv(Nd nd, long id) {
        String dbMapCode = nd.getDbMapCode();
        NdDbMapping ndDbMapping = ndCacheService.getNdDbMapping(dbMapCode);
        String sql = ndDbMapping.getSelectSql() + " where id = :id";
        Session session = this.sessionFactory.getCurrentSession();
        Query q = session.createSQLQuery(sql);
        q.setLong(ConstDef.FD_NAME_ID, id);
        List<Map<String, Object>> resultList = q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
        if (resultList.size() == 0)
            return null;
        Map<String, Object> record = (Map<String, Object>) resultList.get(0);
        Nv nv = this.record2Nv(nd, record, ndDbMapping);
        return nv;
    }

    @Override
    public Nv getNv(String ndCode, long id) {
        Nd nd = this.ndCacheService.getNd(ndCode);
        Nv nv = getSingleNvWithoutFv(nd, id);
        String dbMapCode = nd.getDbMapCode();
        NdDbMapping ndDbMapping = ndCacheService.getNdDbMapping(dbMapCode);
        if (ndDbMapping.getFvTableName() != null && !ndDbMapping.getFvTableName().equals("")) {
            Map<String, Fv> fvs = this.getFvs(nd, nv.getId());
            for (Fv fv : fvs.values()) {
                nv.setFv(fv);
            }
        }
        List<Nd> childrenNd = nd.getChildren();
        for (Nd childNd : childrenNd) {
            if (StringUtils.isEmpty(childNd.getDbMapCode()))
                continue;
            List<Nv> childNvs = getChildNvs(nd.getNdCode(), childNd.getDbMapCode(), childNd.getNdCode(), nv.getId());
            nv.setChildNv(childNd.getNdName(), childNvs);
        }
        if (nd.isCursive()) {
            List<Nv> childNvs = getChildNvs(nd.getNdCode(), nd.getDbMapCode(), nd.getNdCode(), nv.getId());
            nv.setChildNv(nd.getNdName(), childNvs);
        }
        return nv;
    }


    public Map<String, Fv> getFvs(Nd nd, long nvId) {
        NdDbMapping ndDbMapping = ndCacheService.getNdDbMapping(nd.getDbMapCode());
        if (ndDbMapping.getFvTableName() == null || ndDbMapping.getFvTableName().equals(""))
            return null;
        String sql = "select id, nvId,code,name,dataType,intValue, stringValue,doubleValue,dateValue from " + ndDbMapping.getFvTableName() + " where nvId = :nvId";
        Session session = this.sessionFactory.getCurrentSession();
        Query q = session.createSQLQuery(sql);
        q.setLong(ConstDef.FD_NAME_NV_ID, nvId);
        List<Map<String, Object>> resultList = q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
        Map<String, Fv> fvs = new HashMap<String, Fv>();
        for (Map<String, Object> record : resultList) {
            Fv fv = this.record2Fv(nd, record, ndDbMapping);
            fvs.put(fv.getName(), fv);
        }
        return fvs;
    }


    private int filterNvsCount(ListQueryCondition condition) {

        Nd nd = this.ndCacheService.getNd(condition.getNdCode());
        NdDbMapping ndTypeMap = ndCacheService.getNdDbMapping(nd.getDbMapCode());

        String sql = "select count(*)  from " + ndTypeMap.getNvTableName();
        sql += String.format(" where 1 = 1 ");

        for (FieldCondition fieldCondition : condition.getFieldCondition()) {

            String fieldName = fieldCondition.getFdName();

            if (fieldCondition.getLogicalOperation().equals(ConstDef.LOGIC_OPERATION_EQUAL))
                sql += " and " + fieldName + "= :" + fieldName;
            else if (fieldCondition.getLogicalOperation().equals(ConstDef.LOGIC_OPERATION_BIG))
                sql += " and " + fieldName + "> :" + fieldName;
            else if (fieldCondition.getLogicalOperation().equals(ConstDef.LOGIC_OPERATION_LESS))
                sql += " and " + fieldName + "< :" + fieldName;
            else if (fieldCondition.getLogicalOperation().equals(ConstDef.LOGIC_OPERATION_LEFT_LIKE))
                sql += " and " + fieldName + "like :" + fieldName;
            else
                throw new RuntimeException("不存在操作符:" + fieldCondition.getLogicalOperation());

        }


        Session session = this.sessionFactory.getCurrentSession();
        Query q = session.createSQLQuery(sql);

        for (FieldCondition fieldCondition : condition.getFieldCondition()) {

            String fieldName = fieldCondition.getFdName();
            String dataType = null;
            if (fieldName.equals(ConstDef.FD_NAME_ID))
                dataType = ConstDef.DATA_TYPE_LONG;
            else if (fieldName.equals(ConstDef.FD_NAME_META_ND_CODE) || fieldName.equals(ConstDef.FD_NAME_META_ND_NAME) || fieldName.equals(ConstDef.FD_NAME_META_DB_MAP_CODE))
                dataType = ConstDef.DATA_TYPE_STRING;
            else
                dataType = nd.getFdByName(fieldName).getDataType();

            if (dataType.equals(ConstDef.DATA_TYPE_DATE))
                q.setDate(fieldName, (java.util.Date) fieldCondition.getValue());
            else if (dataType.equals(ConstDef.DATA_TYPE_DATETIME))
                q.setTimestamp(fieldName, (java.util.Date) fieldCondition.getValue());
            else if (dataType.equals(ConstDef.DATA_TYPE_FLOAT)) {
                q.setDouble(fieldName, (Double) fieldCondition.getValue());

            } else if (dataType.equals(ConstDef.DATA_TYPE_LONG)) {
                q.setLong(fieldName, (Long) fieldCondition.getValue());
            } else if (dataType.equals(ConstDef.DATA_TYPE_INT)) {
                q.setInteger(fieldName, (Integer) fieldCondition.getValue());
            } else if (dataType.equals(ConstDef.DATA_TYPE_STRING)) {
                q.setString(fieldName, (String) fieldCondition.getValue());
            } else if (dataType.equals(ConstDef.DATA_TYPE_BOOLEAN)) {
                q.setBoolean(fieldName, (Boolean) fieldCondition.getValue());
            }

        }
        try {
            List<BigInteger> list = q.list();
            int count = list.get(0).intValue();
            return count;
        } catch (Exception e) {
            log.error(e.getMessage(), e);
            throw e;
        }

    }


    @Override
    public ListQueryResult<Nv> filterNvs(ListQueryCondition condition) {

        int count = filterNvsCount(condition);

        Nd nd = this.ndCacheService.getNd(condition.getNdCode());
        NdDbMapping ndTypeMap = ndCacheService.getNdDbMapping(nd.getDbMapCode());

        String sql = ndTypeMap.getSelectSql();
        sql += String.format(" where 1=1 ");

        for (FieldCondition fieldCondition : condition.getFieldCondition()) {

            String fieldName = fieldCondition.getFdName();

            if (fieldCondition.getLogicalOperation().equals(ConstDef.LOGIC_OPERATION_EQUAL))
                sql += " and " + fieldName + "= :" + fieldName;
            else if (fieldCondition.getLogicalOperation().equals(ConstDef.LOGIC_OPERATION_BIG))
                sql += " and " + fieldName + "> :" + fieldName;
            else if (fieldCondition.getLogicalOperation().equals(ConstDef.LOGIC_OPERATION_LESS))
                sql += " and " + fieldName + "< :" + fieldName;
            else if (fieldCondition.getLogicalOperation().equals(ConstDef.LOGIC_OPERATION_LEFT_LIKE))
                sql += " and " + fieldName + "like :" + fieldName;
            else
                throw new RuntimeException("不存在操作符:" + fieldCondition.getLogicalOperation());

        }


        Session session = this.sessionFactory.getCurrentSession();
        Query q = session.createSQLQuery(sql);
        for (FieldCondition fieldCondition : condition.getFieldCondition()) {

            String fieldName = fieldCondition.getFdName();
            String dataType = null;
            if (fieldName.equals(ConstDef.FD_NAME_ID) || fieldName.equals(ConstDef.FD_NAME_PARENT_ID))
                dataType = ConstDef.DATA_TYPE_LONG;
            else if (fieldName.equals(ConstDef.FD_NAME_META_ND_CODE) || fieldName.equals(ConstDef.FD_NAME_META_ND_NAME) || fieldName.equals(ConstDef.FD_NAME_META_DB_MAP_CODE))
                dataType = ConstDef.DATA_TYPE_STRING;
            else
                dataType = nd.getFdByName(fieldName).getDataType();


            if (dataType.equals(ConstDef.DATA_TYPE_DATE))
                q.setDate(fieldName, (java.util.Date) fieldCondition.getValue());
            else if (dataType.equals(ConstDef.DATA_TYPE_DATETIME))
                q.setTimestamp(fieldName, (java.util.Date) fieldCondition.getValue());
            else if (dataType.equals(ConstDef.DATA_TYPE_FLOAT)) {
                q.setDouble(fieldName, (Double) fieldCondition.getValue());

            } else if (dataType.equals(ConstDef.DATA_TYPE_LONG)) {
                q.setLong(fieldName, (Long) fieldCondition.getValue());
            } else if (dataType.equals(ConstDef.DATA_TYPE_INT)) {
                q.setInteger(fieldName, (Integer) fieldCondition.getValue());
            } else if (dataType.equals(ConstDef.DATA_TYPE_STRING)) {
                q.setString(fieldName, (String) fieldCondition.getValue());
            } else if (dataType.equals(ConstDef.DATA_TYPE_BOOLEAN)) {
                q.setBoolean(fieldName, (Boolean) fieldCondition.getValue());
            }
        }

        q.setFirstResult(condition.getStartNo());
        q.setMaxResults(condition.getCount());
        List<Map<String, Object>> resultList = (List<Map<String, Object>>) q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();

        ListQueryResult<Nv> queryResult = new ListQueryResult();

        queryResult.setTotalCount(count);
        for (Map<String, Object> record : resultList) {
            Nv nv = filterRecord2Nv(nd, record, ndTypeMap);
            Map<String, Fv> fvs = this.getFvs(nd, nv.getId());
            if (fvs != null) {
                for (Fv fv : fvs.values()) {
                    if (nv.getFvByName(fv.getName()) != null)
                        nv.setFv(fv);
                }

            }
            queryResult.getResult().add(nv);
        }
        return queryResult;
    }


    private Nv filterRecord2Nv(Nd rootNd, Map<String, Object> record, NdDbMapping ndDbMapping) {
        String code = (String) record.get(ConstDef.FD_NAME_META_ND_CODE);
        Nv nv = NodeUtil.nd2Nv(rootNd);

        long id = ((BigInteger) record.get(ConstDef.FD_NAME_ID)).longValue();
        long parentId = 0L;
        if (record.get(ConstDef.FD_NAME_PARENT_ID) != null)
            parentId = ((BigInteger) record.get(ConstDef.FD_NAME_PARENT_ID)).longValue();

        nv.setId(id);
        nv.setParentId(parentId);
        nv.setMetaNdCode(rootNd.getNdCode());
        nv.setActualNdCode(code);

        for (String name : ndDbMapping.getNvFieldNames()) {
            if (nv.getFvByName(name) == null)
                continue;

            Object value = record.get(name);
            Fd fd = rootNd.getFdByName(name);

            if (fd.getDataType().equals(ConstDef.DATA_TYPE_LONG)) {
                long longValue = 0;
                if (value != null)
                    longValue = ((BigInteger) value).longValue();
                nv.setFvByName(name, longValue);
            } else
                nv.setFvByName(name, value);
        }
        return nv;
    }


    public Object getFvByExpression(String expression, String dbMapCode) {
        NdDbMapping ndDbMapping = ndCacheService.getNdDbMapping(dbMapCode);
        String sql = String.format("select %s as  value from ", expression) + ndDbMapping.getNvTableName();
        Session session = this.sessionFactory.getCurrentSession();
        Query q = session.createSQLQuery(sql);
        List<Map<String, Object>> resultList = q.setResultTransformer(Transformers.ALIAS_TO_ENTITY_MAP).list();
        if (resultList.size() == 0)
            return null;
        Map<String, Object> record = resultList.get(0);
        Object value = record.get("value");
        return value;
    }


}
